/*
 * Timer Example
 *
 * Desc: a simple example to create periodic hrtimer
 * Usage: ./test.sh
 *   myapp 1   create a periodic hrtimer
 *   sleep a while
 *   myapp 3   destroy the hrtimer
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>

#include <linux/miscdevice.h>
#include <linux/workqueue.h>
#include <linux/timer.h>
#include <linux/hrtimer.h>

#include "my_common.h"

static struct hrtimer my_hrtimer;

enum hrtimer_restart my_hrtimer_fn(struct hrtimer *hrtimer)
{
	pr_info("current: %lld\n", ktime_get());

	//hrtimer_start(hrtimer, ktime_set(1, 0), HRTIMER_MODE_REL);
	//return HRTIMER_NORESTART;  //start即使返回NORESTART也能在次触发

	hrtimer_forward_now(hrtimer, ktime_set(1, 0));
	return HRTIMER_RESTART; //forward一定需要返回RESTART才会触发
}

static void do_ioctl_cmd1(void)
{
	hrtimer_init(&my_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	my_hrtimer.function = my_hrtimer_fn;
	hrtimer_start(&my_hrtimer, 0, HRTIMER_MODE_REL);  //立即触发
}

static void do_ioctl_cmd2(void)
{
}

static void do_ioctl_cmd3(void)
{
	hrtimer_cancel(&my_hrtimer);
}

static long mydrv_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	switch (cmd) {
	case CMD1:
		do_ioctl_cmd1();
		break;
	case CMD2:
		do_ioctl_cmd2();
		break;
	case CMD3:
		do_ioctl_cmd3();
		break;
	default:
		pr_err("Doesn't support cmd %d\n", cmd);
	}

	return 0;
}

static struct file_operations mydrv_fops = {
	.owner = THIS_MODULE,
	.unlocked_ioctl = mydrv_ioctl,
};


static struct miscdevice mydrv_misc_dev = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = "mydev",
	.fops = &mydrv_fops,
};

static int __init mydrv_init(void)
{
	int ret;

	pr_info("%s...\n", __func__);

	ret = misc_register(&mydrv_misc_dev);
	if (ret) {
		pr_err("%s: misc_register failed, errno=%d\n", __func__, ret);
		return ret;
	}

	return 0;
}

static void __exit mydrv_exit(void)
{
	pr_info("%s...\n", __func__);
	misc_deregister(&mydrv_misc_dev);
}

module_init(mydrv_init);
module_exit(mydrv_exit);

MODULE_AUTHOR("mydrv");
MODULE_LICENSE("GPL");
